package dto

import (
	"osiris/logger"
	"osiris/ocrypto/ed25519"
)

// const DTO状态码
//
//	@param CODE_PING /p2p/ping：发起握手
//	@param CODE_PONG /p2p/ping：ECHO：验证通过，响应握手
//	@param CODE_NODE_DISCOVER /p2p/node/discover：发起节点查询
//	@param CODE_NODE_STORED /p2p/node/discover：ECHO:有目标节点的信息
//	@param CODE_NODE_NOT_STORED /p2p/node/discover：ECHO:无目标节点的信息
//	@param CODE_PEER_EXIST 节点账本（节点状态树）中存在目标节点
//	@param CODE_PEER_NOT_EXIST 节点账本（节点状态树）中不存在目标节点
const (
	CODE_PING = 1001
	CODE_PONG = 1011

	CODE_NODE_DISCOVER   = 1002
	CODE_NODE_STORED     = 1021
	CODE_NODE_NOT_STORED = 1022

	CODE_PEER_EXIST     = 3001
	CODE_PEER_NOT_EXIST = 3002
)

// TODO 要不要管multiaddr不唯一的情况？（多网口、多协议、DHCP、动态NAT……）
// PeerDto 俩节点握手/主动节点发现的DTO
type PeerDto struct {

	// BaseDto
	BaseDto

	// TargetPeerID 目标节点的peer.ID.String()
	TargetPeerID string `json:"targetPeerID"`

	// TargetMultiaddrs 目标节点的ma.Multiaddr.String()
	TargetMultiaddrs []string `json:"targetMultiaddrs"`

	// SenderPeerID 发送方节点的Peer.ID.String()(其它节点收到消息后该向谁回复)
	SenderPeerID string `json:"senderPeerID"`

	// SenderMultiaddrs 发送方节点的ma.Multiaddr.String()（其它节点收到消息后该向谁回复）
	SenderMultiaddrs []string `json:"senderMultiaddrs"`

	// SenderPubKey 发送方节点的公钥(16进制字符串)(ed25519+crypto.Marshal)
	SenderPubKey string `json:"senderPubKey"`

	// SenderSignature 发送方节点对整个PeerDto的hash的签名(ed25519+Base64)
	SenderSignature string `json:"senderSignature"`
}

/*
哪些字段应该计入hash？
除QuerySignature、DtoHash以外的所有字段
DtoHash：加上后就变循环hash了
QuerySignature：加上后签名时就不能对DtoHash签名，无法证明DtoHash所涉及的内容确实是由发起方发出的
*/

// Hash 计算PeerDto的hash
//
//	@receiver peerDto
//	@return string SHA256 hash
//	@return error
func (peerDto PeerDto) Hash() (string, error) {
	peerDto.DtoHash = ""
	peerDto.SenderSignature = ""
	return SHA256Hash(peerDto)
}

// SignPeer 给PeerDto算hash并对hash签名
//
//	@param peerDto
//	@return dto.PeerDto 加了QueryDtoHash和QuerySignature字段的PeerDto
//	@return error
func (peerDto *PeerDto) HashAndSign() error {
	//计算hash
	hasStr, err1 := peerDto.Hash()
	peerDto.DtoHash = hasStr
	if err1 != nil {
		logger.Error(map[string]interface{}{"[dto] [Has And Sign Peer] calculate dto hash": err1})
		return err1
	}

	//计算签名
	sig, err2 := ed25519.ED25519Sign(ed25519.GetPeerPrivKey(), peerDto.DtoHash)
	if err2 != nil {
		logger.Error(map[string]interface{}{"[dto] [[Has And Sign Peer] sign dto hash": err2})
	}

	peerDto.SenderSignature = sig
	return err2
}

// VerifyPeer 验证节点合法性，首先验证hash是否被篡改，其次验证签名合法性
//
//	@param peerDto
//	@return bool
func (peerDto PeerDto) Verify() bool {
	//计算DtoHash
	dtoHash, err1 := peerDto.Hash()
	if err1 != nil {
		logger.Warn(map[string]interface{}{"[dto] [Verify Peer] calculate hash": err1})
		return false
	}

	//验证DtoHash
	if dtoHash != peerDto.DtoHash {
		logger.Warn(map[string]interface{}{"[dto] [Verify Peer] verify hash": "fail"})
		return false
	}

	//拿公钥
	pubKey, err2 := ed25519.StrToPubKey(peerDto.SenderPubKey)
	if err2 != nil {
		logger.Warn(map[string]interface{}{"[dto] [Verify Peer] str to pubKey": err2})
		return false
	}

	//拿签名
	decodedSignature, err3 := ed25519.StrToED25519Sig(peerDto.SenderSignature)
	if err3 != nil {
		logger.Warn(map[string]interface{}{"[dto] [Verify Peer] decode signature by base64": err3})
		return false
	}

	//验证签名
	valid := ed25519.ED25519Verify(pubKey, peerDto.DtoHash, decodedSignature)
	if !valid {
		logger.Warn(map[string]interface{}{"[dto] [Verify Peer] verify sigature": "fail"})
		return false
	}

	return true
}

// MultiPeerDto 用于Bootstrap节点发现的返回Dto
type MultiPeerDto struct {

	// BaseDto
	BaseDto

	// AddrMap 每个peer.ID.String()对应的[]ma.Multiaddr
	AddrMap map[string][]string
}

// PeerStateDto 用于传输节点状态
type PeerStateDto struct {
	// BaseDto
	BaseDto

	// PeerID
	PeerID string `json:"peerID"`

	// Asset 节点的余额（账户质押给的）
	Asset int64 `json:"asset"`

	// CoinSince 用于计算币龄（=time.Now().Unix()-CoinSince）
	CoinSince int64 `json:"coinSince"`
}
